package handler

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"go-devops/common"
	"go-devops/domain/dto"
	"io"
	apierros "k8s.io/apimachinery/pkg/api/errors"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/yaml"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/util/retry"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

type K8sTaskHandler struct {
	CommonTaskHandler
}

func NewK8sTaskHandler() *K8sTaskHandler {
	return &K8sTaskHandler{
		CommonTaskHandler: CommonTaskHandler{},
	}
}

type K8sData struct {
	ApiServer  string `json:"apiServer"`
	SecretName string `json:"secretName"`
	FilePath   string `json:"filePath"`
	WorkDir    string `json:"workDir"`
}

func getK8sData(context *dto.NodeContext) K8sData {
	data := context.TaskData.Data
	jsonString := common.JsonString(data)
	var k8sData K8sData
	k8sData.ApiServer = "https://localhost:6443"
	common.ParseJson(jsonString, &k8sData)
	return k8sData
}

func (c K8sTaskHandler) Stop(context *dto.NodeContext) error {
	context.StopFlag = true
	return nil
}

func (c K8sTaskHandler) HandleNode(nodeContext *dto.NodeContext) error {
	return c.Handle(nodeContext, func() error {
		data := getK8sData(nodeContext)
		log.Printf("HandleNode k8sData:%v\n", data)

		secret, err := GetSecret(common.K8S, data.SecretName)
		if err != nil {
			return err
		}
		log.Printf("HandleNode k8sData:%+v secret:+%v\n", data, secret)

		certData, err := base64.StdEncoding.DecodeString(secret.SecretKey)
		if err != nil {
			return fmt.Errorf("DecodeString err:%v", err)
		}

		keyData, err := base64.StdEncoding.DecodeString(secret.SecretValue)
		if err != nil {
			return fmt.Errorf("DecodeString err:%v", err)
		}

		certificate, err := parseCertificateAndKey(certData, keyData)
		if err != nil {
			return fmt.Errorf("parseCertificateAndKey err:%v", err)
		}

		config := &rest.Config{
			Host: data.ApiServer,
			Transport: &http.Transport{
				TLSClientConfig: &tls.Config{
					InsecureSkipVerify: true,
					Certificates:       []tls.Certificate{*certificate},
				},
			},
		}
		client, err := kubernetes.NewForConfig(config)
		if err != nil {
			return fmt.Errorf("k8s new client err:%v", err)
		}

		version, err := client.ServerVersion()
		if err != nil {
			return fmt.Errorf("k8s get server version err:%v", err)
		}
		fmt.Printf("k8s version:%s\n", version.String())
		sendMsg(nodeContext, true, fmt.Sprintf("k8s version:%s", version.String()))

		workspace := nodeContext.Workspace
		if len(data.WorkDir) > 0 {
			workspace = filepath.Join(workspace, data.WorkDir)
		}
		yamlFile, err := getYamlFile(data, workspace)
		if err != nil {
			return err
		}
		log.Printf("k8s yamlFile:%s\n", yamlFile)
		sendMsg(nodeContext, true, fmt.Sprintf("k8s yamlFile:%s", yamlFile))

		// 创建 Dynamic 客户端
		dynamicClient, err := dynamic.NewForConfig(config)
		if err != nil {
			return fmt.Errorf("error creating dynamic client: %v", err)
		}

		yamlData, err := os.ReadFile(yamlFile) // 使用 os.ReadFile
		if err != nil {
			return fmt.Errorf("read yamlFile err:%v", err)
		}

		// 解析 YAML 文件为 unstructured.Unstructured 对象
		var resources []unstructured.Unstructured
		decoder := yaml.NewYAMLOrJSONDecoder(strings.NewReader(string(yamlData)), 1024)
		for {
			var obj unstructured.Unstructured
			if err := decoder.Decode(&obj); err != nil {
				if err == io.EOF {
					break
				}
				return fmt.Errorf("error unmarshalling YAML data: %v", err)
			}
			resources = append(resources, obj)
		}

		// 应用每个资源
		for _, resource := range resources {
			groupVersionKind := resource.GetObjectKind().GroupVersionKind()

			gvr := schema.GroupVersionResource{
				Group:    groupVersionKind.Group,
				Version:  groupVersionKind.Version,
				Resource: getResourceName(groupVersionKind.Kind),
			}

			resourceName := resource.GetName()
			namespace := resource.GetNamespace()
			if namespace == "" {
				namespace = "default"
			}

			if groupVersionKind.Kind == "Namespace" {
				dynResource := dynamicClient.Resource(gvr)
				if err := handleResource(nodeContext, dynResource, resourceName, &resource); err != nil {
					return err
				}
			} else {
				dynResource := dynamicClient.Resource(gvr).Namespace(namespace)
				if err := handleResource(nodeContext, dynResource, resourceName, &resource); err != nil {
					return err
				}
			}
		}

		return nil
	})
}

func handleResource(nodeContext *dto.NodeContext, dynResource dynamic.ResourceInterface, name string, resource *unstructured.Unstructured) error {
	_, err := dynResource.Create(nodeContext.Context, resource, v1.CreateOptions{})
	if err != nil {
		// 如果资源已存在，则更新它
		if apierros.IsAlreadyExists(err) {
			retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
				result, err := dynResource.Get(nodeContext.Context, name, v1.GetOptions{})
				if err != nil {
					return fmt.Errorf("k8s getting existing resource:%v", err)
				}
				if resource.GetKind() == "Deployment" {
					stringMap, ok, err := unstructured.NestedMap(result.Object, "spec", "template", "metadata", "labels")
					if err != nil || !ok || stringMap == nil {
						return fmt.Errorf("deployment labels not found or error in spec: %v", err)
					}
					if err := unstructured.SetNestedField(stringMap, common.NowStrOther(), "time"); err != nil {
						return err
					}
					if err := unstructured.SetNestedField(result.Object, stringMap, "spec", "template", "metadata", "labels"); err != nil {
						return err
					}
				}
				_, err = dynResource.Update(nodeContext.Context, result, v1.UpdateOptions{})
				if err != nil {
					return fmt.Errorf("k8s update resource:%v", err)
				}
				log.Printf("Updated resource kind:%s name:%s\n", resource.GetKind(), name)
				sendMsg(nodeContext, true, fmt.Sprintf("k8s update resource kind:%s name:%s", resource.GetKind(), name))
				return nil
			})
			return retryErr
		} else {
			return fmt.Errorf("k8s create resource:%v", err)
		}
	} else {
		log.Printf("Created resource kind:%s name:%s\n", resource.GetKind(), name)
		sendMsg(nodeContext, true, fmt.Sprintf("k8s create resource kind:%s name:%s", resource.GetKind(), name))
	}
	return nil
}

func getYamlFile(data K8sData, workspace string) (string, error) {
	yamlFile := data.FilePath
	if len(yamlFile) <= 0 {
		return "", errors.New("部署文件不能为空")
	}

	if strings.Contains(yamlFile, common.EnvWorkspace) {
		return strings.ReplaceAll(yamlFile, common.EnvWorkspace, workspace), nil
	}
	return filepath.Join(workspace, yamlFile), nil
}

// 解析 PEM 格式的证书和私钥
func parseCertificateAndKey(certData []byte, keyData []byte) (*tls.Certificate, error) {
	certBlock, _ := pem.Decode(certData)
	if certBlock == nil {
		return nil, errors.New("failed to parse certificate PEM")
	}

	privateKeyBlock, _ := pem.Decode(keyData)
	if privateKeyBlock == nil {
		return nil, errors.New("failed to parse private key PEM")
	}

	// 解析证书
	cert, err := x509.ParseCertificate(certBlock.Bytes)
	if err != nil {
		return nil, err
	}

	// 解析私钥
	privateKey, err := x509.ParseECPrivateKey(privateKeyBlock.Bytes)
	if err != nil {
		return nil, err
	}

	// 创建 tls.Certificate
	tlsCert := tls.Certificate{
		Certificate: [][]byte{cert.Raw},
		PrivateKey:  privateKey,
	}

	return &tlsCert, nil
}

// 获取资源名称
func getResourceName(kind string) string {
	switch kind {
	case "Deployment":
		return "deployments"
	case "StatefulSet":
		return "statefulsets"
	case "Pod":
		return "pods"
	case "Service":
		return "services"
	case "ConfigMap":
		return "configmaps"
	case "PersistentVolumeClaim":
		return "persistentvolumeclaims"
	case "Ingress":
		return "ingresses"
	case "DaemonSet":
		return "daemonsets"
	case "Job":
		return "jobs"
	case "CronJob":
		return "cronjobs"
	case "Secret":
		return "secrets"
	case "Namespace":
		return "namespaces"
	default:
		return strings.ToLower(kind) + "s"
	}
}
